import 'dart:io';

import 'package:dio/dio.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:gold/http/apis.dart';
import 'package:gold/http/xfg_log_interceptor.dart';
import 'package:gold/utils/xfg_extension.dart';

import 'api_interceptor.dart';
import 'entity_utils.dart';

/// 描述: 网络请求类
/// 
/// @Author tangzhentao
///  @CreatedTime 10:58 2022/6/18
/// 

/// 请求方法
class HttpMethod {
  static const get = 'GET';
  static const post = 'POST';
  static const put = 'PUT';
  static const delete = 'DELETE';
}

class HttpErrorType {
  /// 未知错误
  static const unKnow = -1;
  /// 登录信息过期
  static const invalidToken = -2;
  /// 网络异常
  static const networkError = -3;
  /// 服务器错误
  static const serverError = -4;
  /// 用户取消
  static const cancel = -5;
}

class Http {
  factory Http() => _instance;
  static final Http _instance = Http._internal();
  static Http get instance => _instance;

  static late final Dio _dio;

  bool useBackup = false;

  static const _successCode = 1;

  Http._internal() {
    _dio = _initDio();
  }

  final Duration _timeout = const Duration(seconds: 5);

  Dio _initDio() {
    Dio dio = Dio()
      ..options = BaseOptions(
        connectTimeout: _timeout,
        receiveTimeout: _timeout,
        sendTimeout: _timeout,
        baseUrl: Apis.baseurl,
        contentType: ContentType.json.toString(),
        responseType: ResponseType.json,
      );
    dio.interceptors..add(ApiInterceptor())..add(XfgLogInterceptor(requestHeader: true, requestBody: true, responseBody: true));
    return dio;
  }

  /// 刷新baseUrl
  void refreshBaseUrl(String baseUrl) {
    _dio.options.baseUrl = baseUrl;
  }

  /// 请求返回数据类型是Object
  /// [path] 请求路径
  ///
  /// [query] query参数
  ///
  /// [data] body参数
  ///
  /// [method] 请求方法
  ///
  /// [loadingText] 加载提示语
  ///
  /// [successText] 请求成功提示语
  ///
  /// [cancelToken] 取消请求
  ///
  /// [isShowErrorMsg] 是否提示信息
  Future<Entity<T>> request<T>(
      String path, {
        Map<String, dynamic>? query,
        dynamic data,
        String method = HttpMethod.post,
        String? loadingText,
        String? successText,
        CancelToken? cancelToken,
        bool showErrorMsg = true,
      }) async {
    Entity<T> entity;
    if (loadingText?.isNotEmpty == true) loadingText!.loading();
    try {
      Response response = await _getResponse(method, path, queryParameters: query, data: data, cancelToken: cancelToken);
      /// 数据数据是否为空
      final empty = response.data == null || response.data.toString().isEmpty;
      entity = Entity<T>.fromJson(empty? null : response.data);
      entity.success = entity.code == _successCode;
    } on Exception catch (e) {
      if (e is DioException) {
        entity = _httpErr<T>(e);
      } else if (e is SocketException) {
        /// 服务器异常
        entity = Entity<T>(code: HttpErrorType.networkError,  msg: '网络异常，请稍后重试');
      } else {
        entity = Entity<T>(code: HttpErrorType.serverError,  msg: '服务器错误，请稍后重试');
      }
    }
    if (loadingText?.isNotEmpty == true) EasyLoading.dismiss();
    if (entity.success) {
      successText?.success();
    } else {
      if (showErrorMsg && entity.code != HttpErrorType.cancel) entity.msg.error();
    }
    return entity;
  }

  /// 请求返回数据类型是List
  /// [path] 请求路径
  ///
  /// [query] query参数
  ///
  /// [data] body参数
  ///
  /// [method] 请求方法
  ///
  /// [loadingText] 加载提示语
  ///
  /// [successText] 请求成功提示语
  ///
  /// [cancelToken] 取消请求
  ///
  /// [isShowErrorMsg] 是否提示信息
  Future<ListEntity<T>> requestList<T>(
      String path, {
        Map<String, dynamic>? query,
        dynamic data,
        String method = HttpMethod.post,
        String? loadingText,
        String? successText,
        CancelToken? cancelToken,
        bool showErrorMsg = true,}) async {
    ListEntity<T> entity;
    if (loadingText?.isNotEmpty == true) loadingText!.loading();
    try {
      Response response = await _getResponse(method, path, queryParameters: query, data: data, cancelToken: cancelToken);
      /// 数据数据是否为空
      final empty = response.data == null || response.data.toString().isEmpty;
      entity = ListEntity<T>.fromJson(empty ? null:response.data);
      entity.success = entity.code == _successCode;
    } on Exception catch (e) {
      if (e is DioException) {
        entity = _httpListErr<T>(e);
      } else if (e is SocketException) {
        /// 服务器异常
        entity = ListEntity<T>(code: HttpErrorType.networkError, msg: '网络异常，请稍后重试');
      } else {
        entity = ListEntity<T>(code: HttpErrorType.serverError, msg: '服务器错误，请稍后重试');
      }
    }

    if (loadingText?.isNotEmpty == true) EasyLoading.dismiss();
    if (entity.success) {
      successText?.success();
    } else {
      if (showErrorMsg && entity.code != HttpErrorType.cancel) entity.msg.error();
    }
    return entity;
  }

  /// 请求返回数据类型是Object
  /// [path] 请求路径
  ///
  /// [data] FormData参数
  ///
  /// [loadingText] 加载提示语
  ///
  /// [successText] 请求成功提示语
  ///
  /// [cancelToken] 取消请求
  ///
  /// [isShowErrorMsg] 是否提示信息
  Future<Entity<T>> uploadImage<T>(
      String path,
      FormData data, {
        String? loadingText,
        String? successText,
        bool showErrorMsg = true,
        CancelToken? cancelToken,
      }) async {
    Entity<T> entity;
    if (loadingText?.isNotEmpty == true) loadingText!.loading();
    try {
      Response response = await _dio.post(path, data: data, cancelToken: cancelToken);
      /// 数据数据是否为空
      final empty = response.data == null || response.data.toString().isEmpty;
      entity = Entity<T>.fromJson(empty? [] : response.data);
      entity.success = entity.code == _successCode;
    } on Exception catch (e) {
      if (e is DioException) {
        entity = _httpErr<T>(e);
      } else if (e is SocketException) {
        /// 服务器异常
        entity = Entity<T>(code: HttpErrorType.networkError,  msg: '网络异常，请稍后重试');
      } else {
        entity = Entity<T>(code: HttpErrorType.serverError,  msg: '服务器错误，请稍后重试');
      }
    }
    if (loadingText?.isNotEmpty == true) EasyLoading.dismiss();
    if (entity.success) {
      successText?.success();
    } else {
      if (showErrorMsg && entity.code != HttpErrorType.cancel) entity.msg.error();
    }
    return entity;
  }

  /// 下载文件
  /// [url] 下载路径
  ///
  /// [savePath] 本地保存路径
  ///
  /// [query] query参数
  ///
  /// [loadingText] 加载提示语
  ///
  /// [successText] 请求成功提示语
  ///
  /// [cancelToken] 取消请求
  ///
  /// [isShowErrorMsg] 是否提示信息
  ///
  /// [onReceiveProgress] 下载进度回调
  Future<Entity<int>> download(
      String url,
      String savePath, {
        Map<String, dynamic>? query,
        String? loadingText,
        String? successText,
        CancelToken? cancelToken,
        bool showErrorMsg = true,
        Function(int count, int total)? onReceiveProgress}) async {
    Entity<int> entity;
    try {
      await _dio.download(url, savePath, queryParameters: query, onReceiveProgress: onReceiveProgress, cancelToken: cancelToken);
      entity = Entity(success: true, code: _successCode);
      return entity;
    } on Exception catch (e) {
      if (e is DioException) {
        entity = _httpErr<int>(e);
      } else if (e is SocketException) {
        /// 服务器异常
        entity = Entity(code: HttpErrorType.networkError,  msg: '网络异常，请稍后重试');
      } else {
        entity = Entity(code: HttpErrorType.serverError,  msg: '服务器错误，请稍后重试');
      }
    }
    if (entity.success) {
      successText?.success();
    } else {
      if (showErrorMsg && entity.code != HttpErrorType.cancel) entity.msg.error();
    }
    return entity;
  }

  /// 获得Response
  /// [method] 请求方法
  /// [path] 请求路径
  /// [queryParameters] 请求参数，一般放在地址后用?拼接
  /// [data] 表单数据 一般post会用到
  Future<Response> _getResponse(String method, String path, {Map<String, dynamic>? queryParameters, dynamic data, CancelToken? cancelToken,}) async {
    return await _dio.request(path, queryParameters: queryParameters, data: data, options: Options(method: method), cancelToken: cancelToken);
  }

  /// 根据DioError获取错误信息
  /// [error] 请求异常
  Entity<T> _httpErr<T>(DioException error) {
    var err = '';
    var code = error.response?.statusCode?? HttpErrorType.unKnow;
    switch (error.type) {
      case DioExceptionType.cancel:
        code = HttpErrorType.cancel;
        err = '请求取消';
        break;
      case DioExceptionType.connectionTimeout:
        err = '连接超时';
        break;
      case DioExceptionType.sendTimeout:
        err = '连接超时';
        break;
      case DioExceptionType.receiveTimeout:
        err = '响应超时';
        break;
      case DioExceptionType.connectionError:
        err = '服务器异常';
      case DioExceptionType.unknown:
        err = error.error.toString();
        break;
      case DioExceptionType.badResponse:
        try {
          int errCode = error.response?.statusCode ?? 0;
          switch (errCode) {
            case 400:
              err = '$errCode 请求语法错误';
              break;
            case 401:
              err = '$errCode 登录异常，请重新登录';
              break;
            case 403:
              err = '$errCode 服务器拒绝执行';
              break;
            case 404:
              err = '$errCode 找不到方法';
              break;
            case 405:
              err = '$errCode 请求方法被禁止';
              break;
            case 415:
              err = '$errCode 无效的请求参数';
              break;
            case 500:
              err = '$errCode 服务器内部错误';
              break;
            case 502:
              err = '$errCode 无效的请求';
              break;
            case 503:
              err = '$errCode 服务器挂了';
              break;
            case 505:
              err = '$errCode 不支持HTTP协议请求';
              break;
            default:
              err = '$errCode ${error.response?.statusMessage ?? "未知错误"}';
          }
        } on Exception catch (_) {
          code = HttpErrorType.unKnow;
          err = '未知错误';
        }
        break;
      default:
        code = HttpErrorType.unKnow;
        err = error.error.toString();
    }

    return Entity<T>(code: code, msg: err);
  }

  /// 根据DioError获取错误信息
  /// [error] 请求异常
  ListEntity<T> _httpListErr<T>(DioException error) {
    var err = '';
    var code = error.response?.statusCode?? HttpErrorType.unKnow;
    switch (error.type) {
      case DioExceptionType.cancel:
        code = HttpErrorType.cancel;
        err = '请求取消';
        break;
      case DioExceptionType.connectionTimeout:
        err = '连接超时';
        break;
      case DioExceptionType.sendTimeout:
        err = '连接超时';
        break;
      case DioExceptionType.receiveTimeout:
        err = '响应超时';
        break;
      case DioExceptionType.connectionError:
        err = '服务器异常';
      case DioExceptionType.unknown:
        err = error.error.toString();
        break;
      case DioExceptionType.badResponse:
        try {
          int errCode = error.response?.statusCode ?? 0;
          switch (errCode) {
            case 400:
              err = '$errCode 请求语法错误';
              break;
            case 401:
              err = '$errCode 登录异常，请重新登录';
              break;
            case 403:
              err = '$errCode 服务器拒绝执行';
              break;
            case 404:
              err = '$errCode 找不到方法';
              break;
            case 405:
              err = '$errCode 请求方法被禁止';
              break;
            case 415:
              err = '$errCode 无效的请求参数';
              break;
            case 500:
              err = '$errCode 服务器内部错误';
              break;
            case 502:
              err = '$errCode 无效的请求';
              break;
            case 503:
              err = '$errCode 服务器挂了';
              break;
            case 505:
              err = '$errCode 不支持HTTP协议请求';
              break;
            default:
              err = '$errCode ${error.response?.statusMessage ?? "未知错误"}';
          }
        } on Exception catch (_) {
          code = HttpErrorType.unKnow;
          err = '未知错误';
        }
        break;
      default:
        err = error.error.toString();
    }

    return ListEntity<T>(code: code, msg: err);
  }
}